﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Rendering.Universal;
using ZyGame.Transport.Common;

namespace ZyGame.Transport.TCP
{
    class ChannelAsyncEventArgs : SocketAsyncEventArgs
    {
        TCPChannel channel;

        public Task Task { get; set; }
        public TaskCompletionSource CompletionSource { get; set; }

        public ChannelAsyncEventArgs(TCPChannel channel)
        {
            this.channel = channel;
        }

        public ChannelAsyncEventArgs(TCPChannel channel, TaskCompletionSource taskCompletionSource)
        {
            this.channel = channel;
            Task = taskCompletionSource.Task;
            CompletionSource = taskCompletionSource;
        }

        protected override void OnCompleted(SocketAsyncEventArgs e)
        {
            base.OnCompleted(e);
            switch (LastOperation)
            {
                case SocketAsyncOperation.Connect:
                    channel?.ContentCompletion(this);
                    break;
                case SocketAsyncOperation.Disconnect:
                    channel?.DisConntentCompletion(this);
                    break;
                case SocketAsyncOperation.Receive:
                case SocketAsyncOperation.ReceiveFrom:
                case SocketAsyncOperation.ReceiveMessageFrom:
                    channel?.RecvieData(this);
                    break;
                case SocketAsyncOperation.Send:
                case SocketAsyncOperation.SendTo:
                case SocketAsyncOperation.SendPackets:
                    channel?.SendCompletion(this);
                    break;
            }
        }
    }


    public class TCPChannel : IChannel
    {
        private ushort port;
        private string address;
        private Socket _socket;
        private bool isSended = false;
        private int recv_end_index = 0;
        private byte[] recv = new byte[1024 * 1024 * 10];
        private byte[] recvieCache = new byte[1024 * 10];
        private INetworkSerialized serialized;
        private List<IChannelHandler> networkOutput = new List<IChannelHandler>();
        private Queue<ChannelAsyncEventArgs> waitingSending = new Queue<ChannelAsyncEventArgs>();

        public bool IsConnected => _socket.Connected;

        public TCPChannel(string address, ushort port, INetworkSerialized serialized)
        {
            this.port = port;
            this.address = address;
            this.serialized = serialized;
        }

        public Task Close()
        {
            ChannelAsyncEventArgs args = new ChannelAsyncEventArgs(this, new TaskCompletionSource());
            args.AcceptSocket = _socket;
            _socket.DisconnectAsync(args);
            return args.Task;
        }

        public Task Connect()
        {
            ChannelAsyncEventArgs args = new ChannelAsyncEventArgs(this, new TaskCompletionSource());
            args.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(address), port);
            args.AcceptSocket = _socket;
            _socket.ConnectAsync(args);
            return args.Task;
        }

        internal void DisConntentCompletion(ChannelAsyncEventArgs args)
        {
            Client.Tools.RunningInMainThread(() =>
            {
                for (int i = 0; i < networkOutput.Count; i++)
                {
                    networkOutput[i].HandleInactived(this);
                    networkOutput[i].Dispose();
                }
            });
            args.CompletionSource.Completion();
            _socket = null;
            recvieCache = null;
            recv = null;
            waitingSending.Clear();
            networkOutput.Clear();
            networkOutput = null;
        }

        internal void ContentCompletion(ChannelAsyncEventArgs args)
        {
            Client.Tools.RunningInMainThread(() =>
            {
                for (int i = 0; i < networkOutput.Count; i++)
                {
                    networkOutput[i].HandleActived(this);
                }
            });
            args.CompletionSource.Completion();
        }

        private void OnBeginRecvie()
        {
            SocketAsyncEventArgs socketAsyncEventArgs = new ChannelAsyncEventArgs(this);
            socketAsyncEventArgs.AcceptSocket = _socket;
            socketAsyncEventArgs.SetBuffer(recvieCache, 0, recvieCache.Length);
            _socket.ReceiveAsync(socketAsyncEventArgs);
        }

        internal void RecvieData(ChannelAsyncEventArgs args)
        {
            if (args.BytesTransferred <= 0)
            {
                //todo 这里可能是连接断开了
                return;
            }

            if (recv_end_index + args.BytesTransferred > recv.Length)
            {
                Client.Console.WriteLine("缓冲池炸了");
                return;
            }

            OnBeginRecvie();
            Array.Copy(args.Buffer, 0, recv, recv_end_index, args.BytesTransferred);
            recv_end_index += args.BytesTransferred;
            //todo 尝试解包
        }

        internal void SendCompletion(ChannelAsyncEventArgs args)
        {
            isSended = false;
            args.CompletionSource.Completion();
            if (waitingSending.Count > 0)
            {
                OnStartSend();
            }
        }

        public void RemoveOutput(IChannelHandler handler)
        {
            networkOutput.Remove(handler);
        }

        public Task WriteAndFlush(object message)
        {
            ByteBuf byteBuf = serialized.Serialized(message);
            ChannelAsyncEventArgs socketAsyncEventArgs = new ChannelAsyncEventArgs(this, new TaskCompletionSource());
            socketAsyncEventArgs.SetBuffer(byteBuf.GetRaw(), 0, byteBuf.ReadableBytes());
            socketAsyncEventArgs.AcceptSocket = _socket;
            waitingSending.Enqueue(socketAsyncEventArgs);
            if (isSended is false)
            {
                OnStartSend();
            }

            return socketAsyncEventArgs.Task;
        }

        private void OnStartSend()
        {
            isSended = true;
            _socket.SendAsync(waitingSending.Dequeue());
        }

        public void AddOutput(IChannelHandler handler)
        {
            networkOutput.Add(handler);
        }

        public Task<T> WriteAndFlush<T>(object meesage)
        {
            throw new NotImplementedException();
        }
    }
}